package com.weiyun.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.weiyun.shortlink.admin.common.convention.exception.ClientException;
import com.weiyun.shortlink.admin.common.enums.UserErrorCodeEnum;
import com.weiyun.shortlink.admin.dao.entity.UserDao;
import com.weiyun.shortlink.admin.dao.mapper.UserMapper;
import com.weiyun.shortlink.admin.dto.req.UserLoginReqDto;
import com.weiyun.shortlink.admin.dto.req.UserRegistryReqDto;
import com.weiyun.shortlink.admin.dto.req.UserUpdateReqDto;
import com.weiyun.shortlink.admin.dto.resp.UserLoginResponseDto;
import com.weiyun.shortlink.admin.dto.resp.UserResponseDTO;
import com.weiyun.shortlink.admin.service.GroupService;
import com.weiyun.shortlink.admin.service.UserService;
import lombok.RequiredArgsConstructor;
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.weiyun.shortlink.admin.common.constant.RedisCacheConstant.Lock_USER_REGISTRY_KEY;
import static com.weiyun.shortlink.admin.common.enums.UserErrorCodeEnum.*;

/**
 * 用户接口实现层
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDao> implements UserService {
    private final RBloomFilter<String> rBloomFilterConfigurationFilter;
    private final Redisson redisson;
    private final StringRedisTemplate stringRedisTemplate;
    private final GroupService groupService;

    @Override
    public UserResponseDTO getUserByUserName(String username) {
        LambdaQueryWrapper<UserDao> queryWrapper = Wrappers.lambdaQuery(UserDao.class)
                .eq(UserDao::getUsername, username);
        UserDao userDao = baseMapper.selectOne(queryWrapper);
        if (userDao == null) {
            throw new ClientException(UserErrorCodeEnum.USER_NULL);
        }
        UserResponseDTO result = new UserResponseDTO();
        BeanUtils.copyProperties(userDao, result);
        return result;
    }

    @Override
    public Boolean hasUserName(String username) {
        return rBloomFilterConfigurationFilter.contains(username);
    }

    @Override
    public void Register(UserRegistryReqDto registryReqDto) {
        if (hasUserName(registryReqDto.getUsername())) {
            throw new ClientException(USER_EXIST);
        }
        RLock lock = redisson.getLock(Lock_USER_REGISTRY_KEY + registryReqDto.getUsername());

        if (!lock.tryLock()) {
            throw new ClientException(USER_NAME_EXIST);
        }

        int insert = 0;
        try {
            insert = baseMapper.insert(BeanUtil.toBean(registryReqDto, UserDao.class));
            if (insert < 1) {
                throw new ClientException(USER_EXIST);
            }
            rBloomFilterConfigurationFilter.add(registryReqDto.getUsername());
            groupService.save(registryReqDto.getUsername(), "默认分组");
            return;
        } catch (Exception e) {
            throw new ClientException(USER_SAVE_ERROR);
        } finally {
            lock.unlock();
//            throw new ClientException(USER_NAME_EXIST);
        }

    }

@Override
public void updateFill(UserUpdateReqDto userUpdateReqDto) {
    //TODO 验证当前用户名是否与登录的相同
    LambdaUpdateWrapper<UserDao> updateWrapper = Wrappers.lambdaUpdate(UserDao.class)
            .eq(UserDao::getUsername, userUpdateReqDto.getUsername());
    baseMapper.update(BeanUtil.toBean(userUpdateReqDto, UserDao.class), updateWrapper);
}

@Override
public UserLoginResponseDto login(UserLoginReqDto userLoginReqDto) {
    LambdaQueryWrapper<UserDao> queryWrapper = Wrappers.lambdaQuery(UserDao.class)
            .eq(UserDao::getUsername, userLoginReqDto.getUsername())
            .eq(UserDao::getPassword, userLoginReqDto.getPassword())
            .eq(UserDao::getDelFlag, 0);
    UserDao userDao = baseMapper.selectOne(queryWrapper);
    if (userDao == null) {
        throw new ClientException("用户不存在");
    }
    Map<Object, Object> hasLogin = stringRedisTemplate.opsForHash().entries("login_" + userLoginReqDto.getUsername());
    if (CollUtil.isNotEmpty(hasLogin)) {
        stringRedisTemplate.expire("login_" + userLoginReqDto.getUsername(), 24L, TimeUnit.DAYS);
        String token = hasLogin.keySet().stream().findFirst().map(Object::toString).orElseThrow(() -> new ClientException("登录错误"));
        return new UserLoginResponseDto(token);
    }
    UUID uuid = UUID.randomUUID();

    stringRedisTemplate.opsForHash().put("login_" + userLoginReqDto.getUsername(), uuid.toString(), JSON.toJSONString(userDao));
    stringRedisTemplate.expire("login_" + userLoginReqDto.getUsername(), 24L, TimeUnit.DAYS);
    return new UserLoginResponseDto(uuid.toString());
}

@Override
public Boolean checkLogin(String username, String token) {
//        Object remoteR = stringRedisTemplate.opsForHash().get("login_" + username, "token");
    return stringRedisTemplate.opsForHash().get("login_" + username, token) != null;
}

@Override
public void logout(String username, String token) {
    if (checkLogin(username, token)) {
        stringRedisTemplate.delete("login_" + username);
        return;
    }
    throw new ClientException("用户token不存在或者用户未登录");
}
}
